
/** 
 * @file		SPI.c
 * @brief				
 * @details	Project	: BHS-Ethernet 
 * 
 * 
 * @author	Seven Zeng
 * @date		30/11/2019
 * @copyright	MICC Tech All rights reserved
 * 
 ************************************************************** 
 * @par
 * 
 * 
 ************************************************************** 
 */
 
 
//===============================INCLUDE==========================

#define SPI_DEF
#include "SPI.h"

#ifdef USE_OS
#include "cmsis_os.h"
#endif
#include "main.h"
//================================================================





//=========================TYPE DEFINITIONS=======================


//================================================================




//=========================CONST DEFINITIONS======================


//================================================================




//=========================VARIABLE DECLARATIONS==================




//================================================================




//=========================ROUTINE PROTOTYPES=====================

void SPI_Select(void) {
	LL_GPIO_ResetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
}

void SPI_Deselect(void) {
	LL_GPIO_SetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
}


/**
 * --------------------------------------------------------------------------------
 * @fn void SPI_Init(T_SPI_INFO* pInfo, SPI_TypeDef *SPIx, u32 baud, u32 format)
 * @brief Init SPI, init process handled by generated code is not implemented here
 * @param baud Baud rate
 * @param format Format, TBD
 * @param pInfo Pointer to spi info structure, @ref T_SPI_INFO
 * @param SPIx Pointer SPI instance
 * @return None
 * @par Example
 * @see 
 */
void SPI_Init(T_SPI_INFO* pInfo, SPI_TypeDef *SPIx, u32 baud, u32 format)
{
	pInfo->pInstance = SPIx;

	if (SPIx == SPI1) {
		pInfo->pDMAInstance = DMA1;
		pInfo->txDMAChannel = LL_DMA_CHANNEL_3;
		pInfo->rxDMAChannel = LL_DMA_CHANNEL_2;
	} else {
		// Unimplemented
		while(1);
	}

	// baud and format is to be defined, as the lower level init process 
	// should have been done by the generated code
	pInfo->format = format;
	pInfo->baud = baud;

	
	LL_SPI_EnableDMAReq_RX (SPIx);
	LL_SPI_EnableDMAReq_TX (SPIx);	
	
	LL_SPI_Enable (SPIx);

}


/**
 * --------------------------------------------------------------------------------
 * @fn u8 SPI_ReadByte(T_SPI_INFO* pInfo)
 * @brief Read a byte from the specified by sending a dummy byte
 * @param pInfo @ref T_SPI_INFO
 * @return None
 * @par Example
 * @see 
 */
u8 SPI_ReadByte(T_SPI_INFO* pInfo)
{
	u8 datum;
	
	LL_GPIO_ResetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
	datum = SPI_ReadByte_WithoutSelect (pInfo);
	LL_GPIO_SetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
	return datum;
}

u8 SPI_ReadByte_WithoutSelect(T_SPI_INFO* pInfo)
{
	//LL_GPIO_ResetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
	while (!LL_SPI_IsActiveFlag_TXE (pInfo->pInstance));
	// dummy read to clear RXNE flag
	volatile u8 dummy = LL_SPI_ReceiveData8 (pInfo->pInstance);
	LL_SPI_TransmitData8 (pInfo->pInstance, dummy);
	while(LL_SPI_IsActiveFlag_BSY (pInfo->pInstance));
	//LL_GPIO_SetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
	return LL_SPI_ReceiveData8 (pInfo->pInstance);
}

/**
 * --------------------------------------------------------------------------------
 * @fn u8 SPI_ReadWriteByte(T_SPI_INFO* pInfo, u8 value)
 * @brief Send a byte, and read the response at the same time
 * @param pInfo @ref T_SPI_INFO
 * @param value Byte value to be sent
 * @return Byte value read
 * @par Example
 * @see 
 */
u8 SPI_ReadWriteByte(T_SPI_INFO* pInfo, u8 value)
{
	u8 datum;
	LL_GPIO_ResetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
	datum = SPI_ReadWriteByte_WithoutSelect (pInfo, value);
	LL_GPIO_SetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
	
	return datum;
}

u8 SPI_ReadWriteByte_WithoutSelect(T_SPI_INFO* pInfo, u8 value)
{
	//LL_GPIO_ResetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
	while (!LL_SPI_IsActiveFlag_TXE (pInfo->pInstance));
	LL_SPI_TransmitData8 (pInfo->pInstance, value);
	while(LL_SPI_IsActiveFlag_BSY (pInfo->pInstance));
	//LL_GPIO_SetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);
	
	return LL_SPI_ReceiveData8 (pInfo->pInstance);
}

/**
 * --------------------------------------------------------------------------------
 * @fn void SPI_ReadWriteBlock(T_SPI_INFO* pInfo, u8* pSrc, u8* pDest, u16 count)
 * @brief 
 * @param count Byte count to be transfer
 * @param pDest Pointer to destination buffer
 * @param pInfo @ref T_SPI_INFO
 * @param pSrc Pointer to source buffer
 * @return None
 * @par Example
 * @see 
 */
void SPI_ReadWriteBlock(T_SPI_INFO* pInfo, u8* pSrc, u8* pDest, u16 count)
{
	LL_GPIO_ResetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);

	SPI_ReadWriteBlock_WithoutSelect (pInfo,  pSrc, pDest, count);

	LL_GPIO_SetOutputPin (ETH_CS_GPIO_Port, ETH_CS_Pin);

}

/**
 * --------------------------------------------------------------------------------
 * @fn void SPI_ReadWriteBlock(T_SPI_INFO* pInfo, u8* pSrc, u8* pDest, u16 count)
 * @brief 
 * @param count Byte count to be transfer
 * @param pDest Pointer to destination buffer
 * @param pInfo @ref T_SPI_INFO
 * @param pSrc Pointer to source buffer
 * @return None
 * @par Example
 * @see 
 */
void SPI_ReadWriteBlock_WithoutSelect(T_SPI_INFO* pInfo, u8* pSrc, u8* pDest, u16 count)
{

	LL_DMA_DisableChannel (pInfo->pDMAInstance, pInfo->rxDMAChannel);
	LL_DMA_ConfigAddresses (pInfo->pDMAInstance, pInfo->rxDMAChannel, (u32)&pInfo->pInstance->DR, (u32)pDest, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
	LL_DMA_SetDataLength (pInfo->pDMAInstance, pInfo->rxDMAChannel, count);
	LL_DMA_ClearFlag_GI2 (pInfo->pDMAInstance);
	LL_DMA_ClearFlag_HT2 (pInfo->pDMAInstance);
	LL_DMA_ClearFlag_TC2 (pInfo->pDMAInstance);
	LL_DMA_ClearFlag_TE2 (pInfo->pDMAInstance);


	LL_DMA_DisableChannel (pInfo->pDMAInstance, pInfo->txDMAChannel);
	LL_DMA_ConfigAddresses (pInfo->pDMAInstance, pInfo->txDMAChannel, (u32)pSrc, (u32)&pInfo->pInstance->DR, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
	LL_DMA_SetDataLength (pInfo->pDMAInstance, pInfo->txDMAChannel, count);
	LL_DMA_ClearFlag_GI3 (pInfo->pDMAInstance);
	LL_DMA_ClearFlag_HT3 (pInfo->pDMAInstance);
	LL_DMA_ClearFlag_TC3 (pInfo->pDMAInstance);
	LL_DMA_ClearFlag_TE3 (pInfo->pDMAInstance);

	
	LL_DMA_EnableChannel (pInfo->pDMAInstance, pInfo->rxDMAChannel);
	LL_DMA_EnableChannel (pInfo->pDMAInstance, pInfo->txDMAChannel);

#ifdef USE_OS
	u16 transmitTime = (((u32)count << 3) * 1000) / pInfo->baud;
	if (transmitTime >= 1) {
		osDelay (transmitTime + 1);
	}
#endif
	while (!LL_SPI_IsActiveFlag_TXE(pInfo->pInstance) || LL_SPI_IsActiveFlag_BSY (pInfo->pInstance));


}





//================================================================




